﻿using Microsoft.CSharp;
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;

namespace DbStreamConverter
{
    public class ClassCode
    {
        public string Name { get; set; }
        public List<PropCode> PropList { get; private set; }
        public ClassCode()
        {
            PropList = new List<PropCode>();
        }
    }

    public class PropCode
    {
        public string Name { get; set; }
        public string PropType { get; set; }
        public string Code { get; set; }
    }

    public static class TypeUtil
    {

        public static Assembly CreateTempAssembly(IEnumerable<ClassCode> code)
        {
            var c = new CSharpCodeProvider();//new Dictionary<string, string> { { "CompilerVersion", "v4.0" } });
            var cp = new CompilerParameters();
            cp.GenerateInMemory = true;
            cp.GenerateExecutable = false;
            cp.IncludeDebugInformation = true;

            cp.ReferencedAssemblies.Add("system.dll");
            cp.ReferencedAssemblies.Add("system.xml.dll");
            cp.ReferencedAssemblies.Add("system.data.dll");
            cp.ReferencedAssemblies.Add("system.core.dll");
            cp.ReferencedAssemblies.Add("Microsoft.CSharp.dll");
            cp.ReferencedAssemblies.Add(typeof(TypeUtil).Assembly.Location);
            cp.CompilerOptions = "/t:library";
            cp.GenerateInMemory = true;

            StringBuilder sb = new StringBuilder("");
            sb.AppendLine("using System;");
            sb.AppendLine("using System.Xml;");
            sb.AppendLine("using System.Data;");
            sb.AppendLine("using System.IO;");
            sb.AppendLine("using System.Linq;");
            sb.AppendLine("using System.Text;");
            sb.AppendLine("using System.Collections.Generic;");
            sb.AppendLine("namespace Temp { \n");
            foreach (var cls in code)
            {
                sb.AppendLine(CreateClassCode(cls));
            }
            sb.AppendLine("}");

            var cr = c.CompileAssemblyFromSource(cp, sb.ToString());
            if (cr.Errors.Count > 0)
            {
                Trace.WriteLine(sb.ToString());
                foreach (CompilerError err in cr.Errors)
                {
                    Trace.WriteLine(err.ErrorText);
                }

                var line = "";
                String[] lines = sb.ToString().Split('\n');
                if (lines.Length >= cr.Errors[0].Line && cr.Errors[0].Line > 0) line = lines[cr.Errors[0].Line-1];
                throw new InvalidOperationException(cr.Errors[0].ErrorText + "\n\n" + line);
            }

            System.Reflection.Assembly a = cr.CompiledAssembly;
            return a;
        }

        private static string CreateClassCode(ClassCode cls)
        {
            string name = cls.Name;
            List<PropCode> props = cls.PropList;
            var sb = new StringBuilder();
            sb.AppendLine(string.Format("public class {0} {{", name));
            foreach (var prop in props)
            {
                sb.AppendLine(string.Format("public {0} {1} {{", prop.PropType, prop.Name));
                if (!string.IsNullOrWhiteSpace(prop.Code))
                {
                    sb.AppendLine("get { ");
                    sb.AppendLine(string.Format("  if (_{0} != null) return ({1})_{0}; ", prop.Name, prop.PropType));
                    sb.AppendLine("  return (" + prop.PropType +")(" + prop.Code + "); ");
                    sb.AppendLine("}");
                    sb.AppendLine("set { _" + prop.Name + " = value; }");
                }
                else
                {
                    sb.AppendLine("get; set;");
                }
                sb.AppendLine("}");
                if (prop.Code != null)
                {
                    sb.AppendLine(string.Format("private object _{0};", prop.Name));
                }
                  
            }
            sb.AppendLine("}");
            return sb.ToString();
        }

 
        public static string GetTypeString(Type t)
        {
            if (t.IsGenericType)
            {
                string name;
                int offset = t.FullName.IndexOf("`");
                if (offset >= 0)
                {
                    name = t.FullName.Substring(0, offset);
                }
                else
                {
                    name = t.FullName;
                }
                List<string> p = new List<string>();
                foreach (Type paraType in t.GetGenericArguments())
                {
                    p.Add(GetTypeString(paraType));
                }
                return string.Format("{0}<{1}>", name, string.Join(",", p.ToArray()));
            }
            else
            {
                return t.FullName;
            }
        }
    }
}
